{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "95c6eb01",
   "metadata": {},
   "source": [
    "# CacheBackedEmbeddings\n",
    "\n",
    "Embeddings는 재계산을 피하기 위해 저장되거나 일시적으로 캐시될 수 있습니다.\n",
    "\n",
    "Embeddings를 캐싱하는 것은 `CacheBackedEmbeddings`를 사용하여 수행될 수 있습니다. 캐시 지원 embedder는 embeddings를 키-값 저장소에 캐싱하는 embedder 주변에 래퍼입니다. 텍스트는 해시되고 이 해시는 캐시에서 키로 사용됩니다.\n",
    "\n",
    "`CacheBackedEmbeddings`를 초기화하는 주요 지원 방법은 `from_bytes_store`입니다. 이는 다음 매개변수를 받습니다:\n",
    "\n",
    "- `underlying_embeddings`: 임베딩을 위해 사용되는 embedder.\n",
    "- `document_embedding_cache`: 문서 임베딩을 캐싱하기 위한 `ByteStore` 중 하나.\n",
    "- `namespace`: (선택 사항, 기본값은 `\"\"`) 문서 캐시를 위해 사용되는 네임스페이스. 이 네임스페이스는 다른 캐시와의 충돌을 피하기 위해 사용됩니다. 예를 들어, 사용된 임베딩 모델의 이름으로 설정하세요.\n",
    "\n",
    "**주의**: 동일한 텍스트가 다른 임베딩 모델을 사용하여 임베딩될 때 충돌을 피하기 위해 `namespace` 매개변수를 설정하는 것이 중요합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c5aed2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API 키를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API 키 정보 로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "028a6341",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH08-Embeddings\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf94c21a",
   "metadata": {},
   "source": [
    "## LocalFileStore 에서 임베딩 사용 (영구 보관)\n",
    "\n",
    "먼저, 로컬 파일 시스템을 사용하여 임베딩을 저장하고 FAISS 벡터 스토어를 사용하여 검색하는 예제를 살펴보겠습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a6d2d88",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.storage import LocalFileStore\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain.embeddings import CacheBackedEmbeddings\n",
    "from langchain_community.vectorstores.faiss import FAISS\n",
    "\n",
    "# OpenAI 임베딩을 사용하여 기본 임베딩 설정\n",
    "embedding = OpenAIEmbeddings()\n",
    "\n",
    "# 로컬 파일 저장소 설정\n",
    "store = LocalFileStore(\"./cache/\")\n",
    "\n",
    "# 캐시를 지원하는 임베딩 생성\n",
    "cached_embedder = CacheBackedEmbeddings.from_bytes_store(\n",
    "    underlying_embeddings=embedding,\n",
    "    document_embedding_cache=store,\n",
    "    namespace=embedding.model,  # 기본 임베딩과 저장소를 사용하여 캐시 지원 임베딩을 생성\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db99fa76",
   "metadata": {},
   "outputs": [],
   "source": [
    "# store에서 키들을 순차적으로 가져옵니다.\n",
    "list(store.yield_keys())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a873ccce",
   "metadata": {},
   "source": [
    "문서를 로드하고, 청크로 분할한 다음, 각 청크를 임베딩하고 벡터 저장소에 로드합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "044e6873",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.document_loaders import TextLoader\n",
    "from langchain_text_splitters import CharacterTextSplitter\n",
    "\n",
    "# 문서 로드\n",
    "raw_documents = TextLoader(\"./data/appendix-keywords.txt\").load()\n",
    "# 문자 단위로 텍스트 분할 설정\n",
    "text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n",
    "# 문서 분할\n",
    "documents = text_splitter.split_documents(raw_documents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1442c979",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 코드 실행 시간을 측정합니다.\n",
    "%time db = FAISS.from_documents(documents, cached_embedder)  # 문서로부터 FAISS 데이터베이스 생성"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "487b6402",
   "metadata": {},
   "source": [
    "벡터 저장소를 다시 생성하려고 하면, 임베딩을 다시 계산할 필요가 없기 때문에 훨씬 더 빠르게 처리됩니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7914f363",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 캐싱된 임베딩을 사용하여 FAISS 데이터베이스 생성\n",
    "%time db2 = FAISS.from_documents(documents, cached_embedder)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cf59fa6",
   "metadata": {},
   "source": [
    "## `InmemoryByteStore` 사용 (비영구적)\n",
    "\n",
    "다른 `ByteStore`를 사용하기 위해서는 `CacheBackedEmbeddings`를 생성할 때 해당 `ByteStore`를 사용하면 됩니다.\n",
    "\n",
    "아래에서는, 비영구적인 `InMemoryByteStore`를 사용하여 동일한 캐시된 임베딩 객체를 생성하는 예시를 보여줍니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a58c94a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.embeddings import CacheBackedEmbeddings\n",
    "from langchain.storage import InMemoryByteStore\n",
    "\n",
    "store = InMemoryByteStore()  # 메모리 내 바이트 저장소 생성\n",
    "\n",
    "# 캐시 지원 임베딩 생성\n",
    "cached_embedder = CacheBackedEmbeddings.from_bytes_store(\n",
    "    embedding, store, namespace=embedding.model\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
